using System;
using Community.CsharpSqlite.Entity;

namespace Community.CsharpSqlite
{
    public class MemPool
    {
        /*
    *************************************************************************
    **
    ** This file contains low-level memory & pool allocation drivers 
    **
    ** This file contains implementations of the low-level memory allocation
    ** routines specified in the sqlite3_mem_methods object.
    **
    *************************************************************************
    **  $Header: Community.CsharpSqlite/src/mem_Pool.cs,v bcbd36f24b23 2010/02/18 17:35:24 Noah $
    *************************************************************************
    */

        /*
    ** Like malloc(), but remember the size of the allocation
    ** so that we can find it later using sqlite3MemSize().
    **
    ** For this low-level routine, we are guaranteed that nByte>0 because
    ** cases of nByte<=0 will be intercepted and dealt with by higher level
    ** routines.
    */
#if SQLITE_POOL_MEM
#if TRUE
static byte[] sqlite3MemMalloc(int nByte) { return new byte[nByte]; }
static int[] sqlite3MemMallocInt(int nInt) { return new int[nInt]; }
#else
    static byte[] sqlite3MemMalloc(int nByte)
    {
      byte[] pByte = null;
      int savej = -1;
      int BestSize = int.MaxValue;
      if (nByte > Global.mem0.aByteSize[0])
      {
        int i;
        for (i = 0; i < Global.mem0.aByteSize.Length - 1; i++) if (nByte <= Global.mem0.aByteSize[i]) break;
        Global.mem0.mr_Byte++;
        for (int j = 0; j <= Global.mem0.aByte_used[i]; j++)
          if (Global.mem0.aByte[i][j] != null)
          {
            if (Global.mem0.aByte[i][j].Length == nByte)
            {
              pByte = Global.mem0.aByte[i][j]; Global.mem0.aByte[i][j] = null; Global.mem0.cf_Byte++;
              if (j == Global.mem0.aByte_used[i]) Global.mem0.aByte_used[i]--;
              break;
            }
            if (Global.mem0.aByte[i][j].Length > nByte)
            {
              if (Global.mem0.aByte[i][j].Length < BestSize)
              {
                BestSize = Global.mem0.aByte[i][j].Length;
                savej = j;
              }
            }
          }
        if (pByte == null && savej >= 0)
        {
          pByte = Global.mem0.aByte[i][savej]; Global.mem0.aByte[i][savej] = null; Global.mem0.cf_Byte++;
          Array.Resize(ref pByte, nByte);
        }
        if (Global.mem0.aByte_used[i] >=0 && Global.mem0.aByte[i][Global.mem0.aByte_used[i]] == null) Global.mem0.aByte_used[i]--;
      }
      if (pByte == null) pByte = new byte[nByte];
      return pByte;
    }
    static int[] sqlite3MemMallocInt(int nInt)
    {
      int[] pInt = null;
      int savei = -1;
      int BestSize = int.MaxValue;
      if (nInt >=10)
      {
        Global.mem0.mr_Int++;
        int i;
        for (i = 0; i < Global.mem0.hw_Int; i++)
          if (Global.mem0.aInt[i] != null)
          {
            if (Global.mem0.aInt[i].Length == nInt)
            {
              pInt = Global.mem0.aInt[i]; Global.mem0.aInt[i] = null; Global.mem0.cf_Int++; break;
            }
            if (Global.mem0.aInt[i].Length > nInt)
            {
              if (Global.mem0.aInt[i].Length < BestSize)
              {
                BestSize = Global.mem0.aInt[i].Length;
                savei = i;
              }
            }
          }
        if (pInt == null && savei >= 0)
        {
          pInt = Global.mem0.aInt[savei]; Global.mem0.aInt[savei] = null; Global.mem0.cf_Int++;
        }
      }
      if (pInt == null) pInt = new int[nInt];
      return pInt;
    }
#endif

static Mem sqlite3MemMallocMem(Mem dummy)
    {
      Mem pMem;
      Global.mem0.msMem.alloc++;
      if ( Global.mem0.msMem.next > 0 && Global.mem0.aMem[Global.mem0.msMem.next] != null )
      {
        pMem = Global.mem0.aMem[Global.mem0.msMem.next];
        Global.mem0.aMem[Global.mem0.msMem.next] = null;
        Global.mem0.msMem.cached++;
        Global.mem0.msMem.next--;
      }
      else pMem = new Mem();
      return pMem;
    }
    static BtCursor MemPool.MemMallocBtCursor( BtCursor dummy )
    {
      BtCursor pBtCursor;
      Global.mem0.msBtCursor.alloc++;
      if (Global.mem0.msBtCursor.next > 0 && Global.mem0.aBtCursor[Global.mem0.msBtCursor.next] != null)
      {
        pBtCursor = Global.mem0.aBtCursor[Global.mem0.msBtCursor.next];
        Global.mem0.aBtCursor[Global.mem0.msBtCursor.next] = null;
        Global.mem0.msBtCursor.cached++;
        Global.mem0.msBtCursor.next--;
      }
      else pBtCursor = new BtCursor();
      return pBtCursor;
    }
#endif
        /*
    ** Free memory.
    */
        // -- overloads ---------------------------------------
        private static void sqlite3MemFree<T>(ref T x) where T : class
        {
            x = null;
        }

        private static void sqlite3MemFree(ref string x)
        {
            x = null;
        }

        //

        /*
    ** Like free() but works for allocations obtained from sqlite3MemMalloc()
    ** or sqlite3MemRealloc().
    **
    ** For this low-level routine, we already know that pPrior!=0 since
    ** cases where pPrior==0 will have been intecepted and dealt with
    ** by higher-level routines.
    */
#if SQLITE_POOL_MEM
#if TRUE
    static void sqlite3MemFreeInt(ref int[] pPrior) { pPrior = null; }
#else
    static void sqlite3MemFree(ref byte[] pPrior)
    {
      if (pPrior == null) return;
      if (pPrior.Length > Global.mem0.aByteSize[0])
      {
        int savej = -1;
        int Smallest = int.MaxValue;
        int i;
        for (i = 0; i < Global.mem0.aByteSize.Length - 1; i++) if (pPrior.Length <= Global.mem0.aByteSize[i]) break;
#if DEBUG
for (int j = 0; j < Global.mem0.aByte[i].Length; j++) if (Global.mem0.aByte[i][j] != null && Global.mem0.aByte[i][j] == pPrior) Debugger.Break();
#endif
        Global.mem0.mf_Byte++;
        for (int j = 0; j <= Global.mem0.aByte_used[i]; j++)
        {
          if (Global.mem0.aByte[i][j] == null)
          {
            Global.mem0.aByte[i][j] = pPrior;
            pPrior = null;
            return;
          }
          if (Global.mem0.aByte[i][j].Length < Smallest)
          {
            savej = j;
            Smallest = Global.mem0.aByte[i][j].Length;
          }
        }

        if (Global.mem0.aByte_used[i] < Global.mem0.aByte[i].Length - 1) Global.mem0.aByte[i][++Global.mem0.aByte_used[i]] = pPrior;
        else if (savej >= 0) Global.mem0.aByte[i][savej] = pPrior;
      }
      pPrior = null;
      return;
    }
    static void sqlite3MemFreeInt(ref int[] pPrior)
    {
      if (pPrior == null) return;
      if (pPrior.Length >= 10)
      {
        int savei = -1;
        int Smallest = int.MaxValue;
#if DEBUG
for (int i = 0; i < Global.mem0.aInt.Length; i++) if (Global.mem0.aInt[i] != null && Global.mem0.aInt[i] == pPrior) Debugger.Break();
#endif
        Global.mem0.mf_Int++;
        for (int i = 0; i < Global.mem0.aInt.Length; i++)
        {
          if (Global.mem0.aInt[i] == null)
          {
            Global.mem0.aInt[i] = pPrior;
            pPrior = null;
            return;
          }
          if (Global.mem0.aInt[i].Length < Smallest)
          {
            savei = i;
            Smallest = Global.mem0.aInt[i].Length;
          }
        }
        if (savei >= 0) Global.mem0.aInt[savei] = pPrior;
      }
      pPrior = null;
      return;
    }
#endif
    static void sqlite3MemFreeMem(ref Mem pPrior)
    {
      if (pPrior == null) return;
#if FALSE && DEBUG
for (int i = Global.mem0.msMem.next - 1; i >= 0; i--) if (Global.mem0.aMem[i] != null && Global.mem0.aMem[i] == pPrior) Debugger.Break();
#endif
      Global.mem0.msMem.dealloc++;
      if ( Global.mem0.msMem.next < Global.mem0.aMem.Length - 1 )
        {
          pPrior.db = null;
          pPrior._SumCtx = null;
          pPrior._MD5Context = null;
          pPrior._SubProgram = null;
          pPrior.flags = MEMFlag.MEM_Null;
          pPrior.r = 0;
          pPrior.u.i = 0;
          pPrior.n = 0;
          if (pPrior.zBLOB != null) sqlite3MemFree(ref pPrior.zBLOB);
          Global.mem0.aMem[++Global.mem0.msMem.next] = pPrior;
          if ( Global.mem0.msMem.next > Global.mem0.msMem.max ) Global.mem0.msMem.max = Global.mem0.msMem.next;

        }
      //Array.Resize( ref Global.mem0.aMem, (int)(Global.mem0.hw_Mem * 1.5 + 1 ));
      //Global.mem0.aMem[Global.mem0.hw_Mem] = pPrior;
      //Global.mem0.hw_Mem = Global.mem0.aMem.Length;
      pPrior = null;
      return;
    }
    static void MemPool.MemFreeBtCursor( ref BtCursor pPrior )
    {
      if ( pPrior == null ) return;
#if FALSE && DEBUG
      for ( int i = Global.mem0.msBtCursor.next - 1; i >= 0; i-- ) if ( Global.mem0.aBtCursor[i] != null && Global.mem0.aBtCursor[i] == pPrior ) Debugger.Break();
#endif
      Global.mem0.msBtCursor.dealloc++;
      if ( Global.mem0.msBtCursor.next < Global.mem0.aBtCursor.Length - 1 )
      {
        Global.mem0.aBtCursor[++Global.mem0.msBtCursor.next] = pPrior;
        if ( Global.mem0.msBtCursor.next > Global.mem0.msBtCursor.max ) Global.mem0.msBtCursor.max = Global.mem0.msBtCursor.next;
      }
      //Array.Resize( ref Global.mem0.aBtCursor, (int)(Global.mem0.hw_BtCursor * 1.5 + 1 ));
      //Global.mem0.aBtCursor[Global.mem0.hw_BtCursor] = pPrior;
      //Global.mem0.hw_BtCursor = Global.mem0.aBtCursor.Length;
      pPrior = null;
      return;
    }
    /*
    ** Like realloc().  Resize an allocation previously obtained from
    ** sqlite3MemMalloc().
    **
    ** For this low-level interface, we know that pPrior!=0.  Cases where
    ** pPrior==0 while have been intercepted by higher-level routine and
    ** redirected to xMalloc.  Similarly, we know that nByte>0 becauses
    ** cases where nByte<=0 will have been intercepted by higher-level
    ** routines and redirected to xFree.
    */
    static byte[] sqlite3MemRealloc(ref byte[] pPrior, int nByte)
    {
      //  long p = (long*)pPrior;
      //  Debug.Assert(pPrior!=0 && nByte>0 );
      //  nByte = Utility.ROUND8( nByte );
      //  p = (long*)pPrior;
      //  p--;
      //  p = realloc(p, nByte+8 );
      //  if( p ){
      //    p[0] = nByte;
      //    p++;
      //  }
      //  return (void*)p;
      Array.Resize(ref pPrior, nByte);
      return pPrior;
    }
#else
        /*
** No-op versions of all memory allocation routines
*/

        private static byte[] sqlite3MemMalloc(int nByte)
        {
            return new byte[nByte];
        }

        private static int[] sqlite3MemMallocInt(int nInt)
        {
            return new int[nInt];
        }

        private static Mem sqlite3MemMallocMem(Mem pMem)
        {
            return new Mem();
        }

        private static void sqlite3MemFree(ref byte[] pPrior)
        {
            pPrior = null;
        }

        private static void sqlite3MemFreeInt(ref int[] pPrior)
        {
            pPrior = null;
        }

        private static void sqlite3MemFreeMem(ref Mem pPrior)
        {
            pPrior = null;
        }

        private static int sqlite3MemInit()
        {
            return StatusCode.SQLITE_OK;
        }

        private static void sqlite3MemShutdown()
        {
        }

        public static BtCursor MemMallocBtCursor(BtCursor dummy)
        {
            return new BtCursor();
        }

        public static void MemFreeBtCursor(ref BtCursor pPrior)
        {
            pPrior = null;
        }
#endif

        private static byte[] sqlite3MemRealloc(byte[] pPrior, int nByte)
        {
            Array.Resize(ref pPrior, nByte);
            return pPrior;
        }

        /*
    ** Report the allocated size of a prior return from xMalloc()
    ** or xRealloc().
    */

        private static int sqlite3MemSize(byte[] pPrior)
        {
            //  long p;
            //  if( pPrior==0 ) return 0;
            //  p = (long*)pPrior;
            //  p--;
            //  return p[0];
            return pPrior.Length;
        }

        /*
    ** Round up a request size to the next valid allocation size.
    */

        private static int sqlite3MemRoundup(int n)
        {
            return Utility.ROUND8(n);
        }

        /*
    ** Initialize this module.
    */

        private static int sqlite3MemInit(object NotUsed)
        {
            Helper.UNUSED_PARAMETER(NotUsed);
            if (!Global.Config.bMemstat)
            {
                /* If memory status is enabled, then the malloc.c wrapper will already
        ** hold the STATIC_MEM mutex when the routines here are invoked. */
                Global.mem0.mutex = MutexHelper.MutexAlloc(MutexType.SQLITE_MUTEX_STATIC_MEM);
            }
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Deinitialize this module.
    */

        private static void sqlite3MemShutdown(object NotUsed)
        {
            Helper.UNUSED_PARAMETER(NotUsed);
            return;
        }

        /*
    ** This routine is the only routine in this file with external linkage.
    **
    ** Populate the low-level memory allocation function pointers in
    ** Global.Config.m with pointers to the routines in this file.
    */

        public static void MemSetDefault()
        {
            var defaultMethods = new sqlite3_mem_methods(
                sqlite3MemMalloc,
                sqlite3MemMallocInt,
                sqlite3MemMallocMem,
                sqlite3MemFree,
                sqlite3MemFreeInt,
                sqlite3MemFreeMem,
                sqlite3MemRealloc,
                sqlite3MemSize,
                sqlite3MemRoundup,
                sqlite3MemInit,
                sqlite3MemShutdown,
                0
                );
            ConfigHelper.Sqlite3Config(ConfigOption.SQLITE_CONFIG_MALLOC, defaultMethods);
        }

        public static void DbFree(sqlite3 db, ref int[] pPrior)
        {
            if (pPrior != null) sqlite3MemFreeInt(ref pPrior);
        }

        public static void DbFree(sqlite3 db, ref Mem pPrior)
        {
            if (pPrior != null) sqlite3MemFreeMem(ref pPrior);
        }

        public static void DbFree(sqlite3 db, ref Mem[] pPrior)
        {
            if (pPrior != null) for (int i = 0; i < pPrior.Length; i++) sqlite3MemFreeMem(ref pPrior[i]);
        }

        public static void DbFree<T>(sqlite3 db, ref T pT) where T : class
        {
        }

        public static void DbFree(sqlite3 db, ref string pString)
        {
        }
    }
}